# -*- coding:utf-8 -*-
__author__ = 'xigu007'
__all__ = ('StaggeredGridLayoutManager',)

from kivy.properties import NumericProperty
from collections import defaultdict, deque
from recycleview import RecycleLayoutManager
from math import ceil

class StaggeredGridLayoutManager(RecycleLayoutManager):
    """StaggeredGridLayoutManager是交错表格排版
    """
    cols = NumericProperty(2)
    reach_bottom = False
    reach_top = False

    view_port_bottom = 0
    view_port_top = 0
    view_bottom = []
    view_top = []
    start_index = []
    visible_views_index = defaultdict(deque)
    trace_index = defaultdict(list)

    def on_cols(self,instance, value):
        recycleview = self.recycleview
        if recycleview:
            recycleview.ask_refresh_all()

    def refresh_view_height(self, view_index):
        recycleview = self.recycleview
        adapter = recycleview.adapter
        views = adapter.views
        if views.has_key(view_index):
            recycleview._handle_ask_data_refresh(extent='data_size')

    def compute_positions_and_sizes(self, append):
        """当data发送变化时，当前显示views的index全部更改，并且清空dirtview
        这里不会重新计算positions和sizes
        """
        recycleview = self.recycleview
        container = recycleview.container
        container.width = recycleview.width
        self.reach_bottom = False
        self.reach_top = False
        self.clear_layout()
        adapter = recycleview.adapter
        adapter.make_views_dirty()
        dirty_views = adapter.dirty_views
        for _key, instances in dirty_views.items():
            for _index, view in instances.items():
                view.Changed = True
        self.view_port_top, self.view_port_bottom = self._get_view_port_bottom_top(recycleview.height, container.height, recycleview.scroll_y)
        visible_views_index = self.visible_views_index
        #从上往下重新布局views
        self.view_bottom = self.view_top[:]
        self.start_index = [indexes[0] for indexes in visible_views_index.values() if indexes]
        self.view_port_bottom += 20  #强制上拉一下，重新布局
        self.visible_views_index.clear()
        #print 'compute_positions_and_sizes start_index={}'.format(self.start_index)

    def _reset_manager(self,recycleview):
        self.view_top = [0]*self.cols
        self.view_bottom = [0]*self.cols
        self.start_index = []
        self.trace_index.clear()
        self.visible_views_index.clear()
        adapter = recycleview.adapter
        if adapter:
            adapter.invalidate()
        container = recycleview.container
        container.height = 0
        self._modify_container_h_from_bottom(recycleview, recycleview.height)

    def recycleview_setup(self):
        """
        StaggeredGridLayoutManager只能上下滚动
        """
        self.orientation = "vertical"
        recycleview = self.recycleview
        recycleview.do_scroll_x = False
        recycleview.do_scroll_y = True
        self._reset_manager(recycleview)

    def _get_view_port_bottom_top(self, secycleview_h, container_h, scroll_y):
        px_bottom = max(0, (container_h - secycleview_h) * scroll_y)
        px_top = px_bottom + min(secycleview_h, container_h)
        return px_top, px_bottom

    def compute_visible_views(self):
        recycleview = self.recycleview
        adapter = recycleview.adapter
        container = recycleview.container
        if recycleview.scroll_y < 0.0 or recycleview.scroll_y > 1.0:
            #print '-----over scroll = {}'.format(recycleview.scroll_y)
            return
        new_view_port_top, new_view_port_bottom  = self._get_view_port_bottom_top(recycleview.height, container.height, recycleview.scroll_y)
        max_bottom = max(self.view_bottom)
        min_top = min(self.view_top)
        new = []
        if new_view_port_bottom  > self.view_port_bottom:
            '''正在向下滚动
           '''
            self.view_port_top = new_view_port_top
            self.view_port_bottom  = new_view_port_bottom
            if self.view_port_top >= min_top:
                new = self._expanding_top(adapter, recycleview)
            old = self._reducing_bottom(adapter)

        else:
            '''正在向上滚动
           '''
            self.view_port_top = new_view_port_top
            self.view_port_bottom  = new_view_port_bottom
            if self.view_port_bottom < max_bottom:
                new = self._expanding_bottom(adapter, recycleview)
            old = self._reducing_top(adapter)

        rm = container.remove_widget
        for widget in old:
            rm(widget)

        add = container.add_widget
        for widget in new:
            # add to the container if it's not already done
            if widget.parent is None:
                add(widget)

    def _modify_container_h_from_bottom(self, recycleview, diff_h):
        """

        :return:
        """
        diff_h = int(diff_h)
        if 1 > abs(diff_h):
            print 'modify height from bottom, but not changed'
            return
        container = recycleview.container
        self.view_bottom = [(h + diff_h) for h in self.view_bottom]
        self.view_top = [(h + diff_h) for h in self.view_top]
        self.view_port_bottom += diff_h
        self.view_port_top += diff_h
        if container.height + diff_h - self.view_port_bottom  < recycleview.height:
            self.view_port_bottom = container.height + diff_h - recycleview.height
            print '----modify vp_b={}'.format(self.view_port_bottom)
        adapter = recycleview.adapter
        views = adapter.views
        for index, view in views.items():
            view.y += diff_h
        sw = (container.height + diff_h - recycleview.height)
        if sw >= 1:
            new_scroll_y = self.view_port_bottom / float(sw)
            recycleview.scroll_y = min(1, max(new_scroll_y, 0))
            print '---bottom- new_scroll_y={}'.format(recycleview.scroll_y)
        container.height += diff_h


    def _modify_container_h_from_top(self, recycleview, diff_h):
        diff_h = int(diff_h)
        if 1 > abs(diff_h):
            print 'modify height from top, but not changed'
            return
        container = recycleview.container
        sw = (container.height + diff_h - recycleview.height)
        if sw >= 1:
            new_scroll_y = self.view_port_bottom / float(sw)
            recycleview.scroll_y = min(1, max(new_scroll_y, 0))
            print '---top- new_scroll_y={}'.format(new_scroll_y)
        container.height += diff_h

    def _expanding_bottom(self, adapter, recycleview):
        new_views = []
        container = recycleview.container
        visible_views = adapter.views
        visible_views_index = self.visible_views_index
        view_bottom = self.view_bottom
        view_w = container.width / float(self.cols)
        end_indexes = [indexes[-1] for indexes in visible_views_index.values() if indexes]
        if end_indexes:
            next_index = max(end_indexes) + 1
        else:
            print '--refresh all views--'
            next_index = 0
            if self.start_index:
                #必须先刷新每一列的第一个view否则索引缓存会乱掉
                for cols, end_index in enumerate(self.start_index):
                    view = adapter.get_view(end_index)
                    view.Index = end_index
                    view_h = view.height
                    if view.Changed:
                        view_h =  view.compute_height(recycleview, end_index, view_w)
                        view.Changed = False
                    view_port = (0, container.width, self.view_port_top, self.view_port_bottom)
                    view.refresh_view_layout(recycleview,end_index,(view_w*cols, self.view_bottom[cols] - view_h), (view_w, view_h), view_port)
                    last_view_h = view.height
                    visible_views[end_index] = view
                    visible_views_index[cols].append(end_index)
                    new_views.append(view)
                    view_bottom[cols] -= last_view_h
                    next_index = end_index + 1 if next_index < end_index + 1 else next_index
        data_len = len(adapter.data)
        view_bottom = self.view_bottom
        max_bottom = max(view_bottom)
        max_cols = view_bottom.index(max_bottom)
        while max_bottom >= self.view_port_bottom:
            if next_index < data_len:
                view = adapter.get_view(next_index)
                view.Index = next_index
                view_h = view.height
                if view.Changed:
                    view_h =  view.compute_height(recycleview, next_index, view_w)
                    view.Changed = False
                view_port = (0, container.width, self.view_port_top, self.view_port_bottom)
                view.refresh_view_layout(recycleview, next_index, (view_w*max_cols, max_bottom - view_h), (view_w, view_h), view_port)
                visible_views[next_index] = view
                visible_views_index[max_cols].append(next_index)
                new_views.append(view)
                view_bottom[max_cols] -= view_h
                if view_bottom[max_cols] < 0:
                    #container长高度不够扩展高度
                    diff_h = view_h*(ceil((data_len - next_index) / float(self.cols)))
                    self._modify_container_h_from_bottom(recycleview, diff_h)
                    view_bottom = self.view_bottom
                max_bottom = max(view_bottom)
                max_cols = view_bottom.index(max_bottom)
                next_index = next_index + 1
            else:
                #已经到最后一个数据,去掉container多余的高度
                if not self.reach_bottom:
                    diff_h = -min(view_bottom)
                    print 'reach the bottom {}, diff_h={}'.format(self.view_port_bottom, diff_h)
                    self._modify_container_h_from_bottom(recycleview, diff_h)
                    view_bottom = self.view_bottom
                    self.reach_bottom = True
                else:
                    break
        return new_views

    def _align_top_views(self, view_indexes, view_top, view_bottom, max_top, adapter, recycleview):
        all_views = adapter.views
        expand = False
        for cols, v_t in enumerate(view_top):
            diff = max_top - v_t
            if diff > 1:
                view_top[cols] += diff
                view_bottom[cols] += diff
                for cols_index in view_indexes[cols]:
                    views = all_views[cols_index]
                    views.y += diff
                if view_top[cols] > self.view_port_bottom:
                    expand = True
        if expand:
            print '--align_top_views_ expanding bottom-'
            self._expanding_bottom(adapter, recycleview)


    def _expanding_top(self, adapter, recycleview):
        new_views = []
        container = recycleview.container
        visible_views = adapter.views
        view_top = self.view_top
        view_bottom = self.view_bottom
        visible_views_index = self.visible_views_index
        trace_index = self.trace_index
        view_w = container.width / float(self.cols)
        trace = 0
        for cols, v_t in enumerate(view_top):
            while v_t <= self.view_port_top:
                trace_list = trace_index[cols]
                if trace_list:
                    trace = trace_list.pop()
                    view = adapter.get_view(trace)
                    view.Index = trace
                    view_h = view.height
                    if view.Changed:
                        view_h =  view.compute_height(recycleview, trace, view_w)
                        view.Changed = False
                    view_port = (0, container.width, self.view_port_top, self.view_port_bottom)
                    view.refresh_view_layout(recycleview,trace,(view_w*cols, v_t), (view_w, view_h), view_port)
                    #view.bind_view_to_top(recycleview, trace, view_w*cols, view_w, v_t, self.view_port_top, self.view_port_bottom)
                    last_view_h = view.height
                    visible_views[trace] = view
                    visible_views_index[cols].appendleft(trace)
                    new_views.append(view)
                    v_t += last_view_h
                    view_top[cols] = v_t
                    if view_top[cols] > container.height:
                        #container长高度不够扩展高度
                        diff_h = last_view_h*ceil((trace + 1)/ float(self.cols))
                        self._modify_container_h_from_top(recycleview, diff_h)
                        view_top = self.view_top
                elif 0 == trace:
                    #已经到第一个数据,去掉container多余的高度
                    if not self.reach_top:
                        print 'reach the top{}'.format(self.view_port_top)
                        max_top = max(view_top)
                        self._modify_container_h_from_top(recycleview, (max_top - container.height))
                        max_top = max(self.view_top)
                        self._align_top_views(visible_views_index, self.view_top, self.view_bottom, max_top, adapter, recycleview)
                        self.reach_top = True
                    else:
                        break
        return new_views

    def _reducing_top(self,adapter):
        old_views =[]
        visible_views = adapter.views
        visible_views_index = self.visible_views_index
        make_view_dirty = adapter.make_view_dirty
        trace_index = self.trace_index
        for cols, v_t in enumerate(self.view_top):
            if visible_views_index[cols]:
                index = visible_views_index[cols][0]
                view = visible_views[index]
                ht = view.height
                while v_t - ht  > self.view_port_top:
                    v_t -= ht
                    self.view_top[cols] = v_t
                    #print '_brc_top view_port_top={}, v_t={}, index={}'.format(self.view_port_top,v_t, index)
                    visible_views.pop(index)
                    trace = visible_views_index[cols].popleft()
                    trace_index[cols].append(trace)
                    old_views.append(view)
                    make_view_dirty(view, index)
                    if not visible_views_index[cols]:
                        print 'top cur cols={} is empty'.format(cols)
                        break
                    index = visible_views_index[cols][0]
                    view = visible_views[index]
                    ht = view.height
        return old_views


    def _reducing_bottom(self,adapter):
        old_views =[]
        visible_views = adapter.views
        make_view_dirty = adapter.make_view_dirty
        visible_views_index = self.visible_views_index
        for cols, v_b in enumerate(self.view_bottom):
            if visible_views_index[cols]:
                index = visible_views_index[cols][-1]
                view = visible_views[index]
                ht = view.height
                while v_b + ht  < self.view_port_bottom:
                    v_b += ht
                    self.view_bottom[cols] = v_b
                    #print '_brc_bottom view_port_top={}, v_b={}, index={}'.format(self.view_port_bottom,v_b, index)
                    visible_views.pop(index)
                    visible_views_index[cols].pop()
                    old_views.append(view)
                    make_view_dirty(view, index)
                    if not visible_views_index[cols]:
                        print 'bottom cur cols={} is empty'.format(cols)
                        break
                    index = visible_views_index[cols][-1]
                    view = visible_views[index]
                    ht = view.height
        return old_views


